import Taro from '@tarojs/taro'

// 错误类型
export enum ErrorType {
  NETWORK = 'network',
  DATABASE = 'database',
  AUTH = 'auth',
  VALIDATION = 'validation',
  BUSINESS = 'business',
  UNKNOWN = 'unknown'
}

// 错误详情
export interface ErrorDetails {
  type: ErrorType;
  code?: string | number;
  message: string;
  data?: any;
  timestamp: number;
  retry?: boolean;
}

// 重试配置
export interface RetryConfig {
  maxRetries: number;
  delayMs: number;
  backoffFactor: number;
}

// 默认重试配置
const DEFAULT_RETRY_CONFIG: RetryConfig = {
  maxRetries: 3,
  delayMs: 1000,
  backoffFactor: 1.5
}

// 错误处理工具类
export class ErrorHandler {
  // 存储最近的错误
  private static recentErrors: ErrorDetails[] = []
  private static maxRecentErrors = 10

  /**
   * 处理错误
   * @param error 错误对象
   * @param type 错误类型
   * @param options 额外选项
   * @returns 格式化后的错误详情
   */
  static handleError(error: any, type: ErrorType = ErrorType.UNKNOWN, options?: {
    showToast?: boolean;
    logToConsole?: boolean;
    data?: any;
    retry?: boolean;
  }): ErrorDetails {
    const opts = {
      showToast: true,
      logToConsole: true,
      ...options
    }

    // 解析错误信息
    let message = '未知错误'
    let code = 'UNKNOWN'

    if (error instanceof Error) {
      message = error.message
    } else if (typeof error === 'string') {
      message = error
    } else if (error && typeof error === 'object') {
      message = error.errMsg || error.message || error.error || '未知错误'
      code = error.code || error.errCode || 'UNKNOWN'
    }

    // 创建错误详情
    const errorDetails: ErrorDetails = {
      type,
      code,
      message,
      data: opts.data,
      timestamp: Date.now(),
      retry: opts.retry
    }

    // 添加到最近错误列表
    this.addToRecentErrors(errorDetails)

    // 控制台日志
    if (opts.logToConsole) {
      console.error(`[${type}] ${message}`, error)
    }

    // 显示提示
    if (opts.showToast) {
      Taro.showToast({
        title: this.getErrorMessage(errorDetails),
        icon: 'none',
        duration: 2000
      })
    }

    return errorDetails
  }

  /**
   * 处理网络错误
   * @param error 错误对象
   * @param options 额外选项
   * @returns 格式化后的错误详情
   */
  static handleNetworkError(error: any, options?: {
    showToast?: boolean;
    logToConsole?: boolean;
    data?: any;
    retry?: boolean;
  }): ErrorDetails {
    // 网络错误特殊处理
    let message = '网络连接失败'
    let code = 'NETWORK_ERROR'

    if (error && typeof error === 'object') {
      if (error.errMsg) {
        if (error.errMsg.includes('timeout')) {
          message = '请求超时，请稍后再试'
          code = 'REQUEST_TIMEOUT'
        } else if (error.errMsg.includes('fail')) {
          message = '网络请求失败，请检查网络连接'
          code = 'REQUEST_FAILED'
        }
      }
      
      if (error.statusCode) {
        code = `HTTP_${error.statusCode}`
        
        switch (error.statusCode) {
          case 401:
            message = '登录已过期，请重新登录'
            break
          case 403:
            message = '没有权限访问该资源'
            break
          case 404:
            message = '请求的资源不存在'
            break
          case 500:
            message = '服务器错误，请稍后再试'
            break
          default:
            if (error.statusCode >= 500) {
              message = '服务器错误，请稍后再试'
            } else if (error.statusCode >= 400) {
              message = '请求参数错误'
            }
        }
      }
    }

    return this.handleError(
      { ...error, message, code },
      ErrorType.NETWORK,
      { ...options, retry: options?.retry ?? true }
    )
  }

  /**
   * 使用重试机制执行函数
   * @param fn 要执行的异步函数
   * @param config 重试配置
   * @returns Promise<T>
   */
  static async withRetry<T>(
    fn: () => Promise<T>,
    config: Partial<RetryConfig> = {}
  ): Promise<T> {
    const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...config }
    let lastError: any
    
    for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
      try {
        // 如果不是第一次尝试，则等待
        if (attempt > 0) {
          const delay = retryConfig.delayMs * Math.pow(retryConfig.backoffFactor, attempt - 1)
          await new Promise(resolve => setTimeout(resolve, delay))
          console.log(`重试 (${attempt}/${retryConfig.maxRetries})...`)
        }
        
        return await fn()
      } catch (error) {
        lastError = error
        console.error(`尝试 ${attempt + 1}/${retryConfig.maxRetries + 1} 失败:`, error)
        
        // 如果是最后一次尝试，则抛出错误
        if (attempt === retryConfig.maxRetries) {
          throw error
        }
        
        // 某些错误类型不应该重试
        if (error && typeof error === 'object') {
          // 不重试认证错误
          if (error.statusCode === 401 || error.statusCode === 403) {
            throw error
          }
        }
      }
    }
    
    throw lastError
  }

  /**
   * 包装 Taro.request 添加重试和错误处理
   * @param options 请求选项
   * @returns Promise<any>
   */
  static async request(options: Taro.request.Option): Promise<any> {
    try {
      return await this.withRetry(async () => {
        const response = await Taro.request(options)
        
        // 检查响应状态码
        if (response.statusCode < 200 || response.statusCode >= 300) {
          throw {
            statusCode: response.statusCode,
            errMsg: `请求失败: ${response.statusCode}`,
            data: response.data
          }
        }
        
        return response.data
      })
    } catch (error) {
      throw this.handleNetworkError(error, { showToast: true })
    }
  }

  /**
   * 包装云函数调用添加重试和错误处理
   * @param name 云函数名称
   * @param data 参数
   * @returns Promise<any>
   */
  static async callCloudFunction(name: string, data: any = {}): Promise<any> {
    try {
      return await this.withRetry(async () => {
        const result = await Taro.cloud.callFunction({
          name,
          data
        })
        
        // 检查云函数返回的业务状态
        if (result.result && typeof result.result === 'object' && result.result.success === false) {
          throw {
            code: 'CLOUD_FUNCTION_BUSINESS_ERROR',
            errMsg: result.result.error || '云函数执行失败',
            data: result.result
          }
        }
        
        return result.result
      })
    } catch (error) {
      throw this.handleError(error, ErrorType.DATABASE, { showToast: true })
    }
  }

  /**
   * 添加错误到最近错误列表
   * @param error 错误详情
   */
  private static addToRecentErrors(error: ErrorDetails): void {
    this.recentErrors.unshift(error)
    
    // 保持列表长度限制
    if (this.recentErrors.length > this.maxRecentErrors) {
      this.recentErrors.pop()
    }
  }

  /**
   * 获取最近的错误列表
   * @returns 最近的错误列表
   */
  static getRecentErrors(): ErrorDetails[] {
    return [...this.recentErrors]
  }

  /**
   * 清除最近的错误列表
   */
  static clearRecentErrors(): void {
    this.recentErrors = []
  }

  /**
   * 获取用户友好的错误消息
   * @param error 错误详情
   * @returns 用户友好的错误消息
   */
  static getErrorMessage(error: ErrorDetails): string {
    // 可以根据错误类型和代码返回更友好的消息
    return error.message
  }
}

// 导出工具函数
export const errorUtils = {
  handleError: ErrorHandler.handleError.bind(ErrorHandler),
  handleNetworkError: ErrorHandler.handleNetworkError.bind(ErrorHandler),
  withRetry: ErrorHandler.withRetry,
  request: ErrorHandler.request.bind(ErrorHandler),
  callCloudFunction: ErrorHandler.callCloudFunction.bind(ErrorHandler),
  getRecentErrors: ErrorHandler.getRecentErrors.bind(ErrorHandler),
  clearRecentErrors: ErrorHandler.clearRecentErrors.bind(ErrorHandler),
  getErrorMessage: ErrorHandler.getErrorMessage.bind(ErrorHandler)
} 